| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355 |
- <template>
- <div class="admin--event-form">
- <div v-if="isLoading" class="admin--loading">
- 데이터를 불러오는 중...
- </div>
- <form v-else @submit.prevent="handleSubmit" class="admin--form">
- <!-- 카테고리 -->
- <div class="admin--form-group">
- <label class="admin--form-label">카테고리 <span class="admin--required">*</span></label>
- <select v-model="formData.category" class="admin--form-select" required>
- <option value="">카테고리를 선택하세요</option>
- <option value="진행중">진행중</option>
- <option value="완료">완료</option>
- </select>
- </div>
- <!-- 댓글허용 -->
- <div class="admin--form-group">
- <label class="admin--form-label">댓글허용</label>
- <div class="admin--checkbox-group">
- <label class="admin--checkbox-label">
- <input v-model="formData.allow_comment" type="checkbox">
- <span>댓글 허용</span>
- </label>
- </div>
- </div>
- <!-- 공지 -->
- <div class="admin--form-group">
- <label class="admin--form-label">공지</label>
- <div class="admin--checkbox-group">
- <label class="admin--checkbox-label">
- <input v-model="formData.is_notice" type="checkbox">
- <span>공지글로 등록</span>
- </label>
- </div>
- </div>
- <!-- 이름 -->
- <div class="admin--form-group">
- <label class="admin--form-label">이름 <span class="admin--required">*</span></label>
- <input
- v-model="formData.name"
- type="text"
- class="admin--form-input"
- placeholder="이름을 입력하세요"
- required
- >
- </div>
- <!-- 이메일 -->
- <div class="admin--form-group">
- <label class="admin--form-label">이메일 <span class="admin--required">*</span></label>
- <input
- v-model="formData.email"
- type="email"
- class="admin--form-input"
- placeholder="이메일을 입력하세요"
- required
- >
- </div>
- <!-- 기간 -->
- <div class="admin--form-group">
- <label class="admin--form-label">기간 <span class="admin--required">*</span></label>
- <div class="admin--date-range">
- <DatePicker
- v-model="formData.start_date"
- placeholder="시작일 선택"
- :max-date="formData.end_date"
- required
- />
- <span class="admin--date-separator">~</span>
- <DatePicker
- v-model="formData.end_date"
- placeholder="종료일 선택"
- :min-date="formData.start_date"
- required
- />
- </div>
- </div>
- <!-- 제목 -->
- <div class="admin--form-group">
- <label class="admin--form-label">제목 <span class="admin--required">*</span></label>
- <input
- v-model="formData.title"
- type="text"
- class="admin--form-input"
- placeholder="제목을 입력하세요"
- required
- >
- </div>
- <!-- 내용 -->
- <div class="admin--form-group">
- <label class="admin--form-label">내용 <span class="admin--required">*</span></label>
- <SunEditor v-model="formData.content" />
- </div>
- <!-- 기존 첨부파일 -->
- <div v-if="existingFiles.length > 0" class="admin--form-group">
- <label class="admin--form-label">기존 첨부파일</label>
- <div class="admin--file-list">
- <div v-for="(file, index) in existingFiles" :key="'existing-' + index" class="admin--file-item">
- <a :href="file.url" target="_blank" class="admin--file-name">{{ file.name }}</a>
- <span class="admin--file-size">({{ formatFileSize(file.size) }})</span>
- <button type="button" class="admin--btn-remove-file" @click="removeExistingFile(index)">
- 삭제
- </button>
- </div>
- </div>
- </div>
- <!-- 새 파일첨부 -->
- <div class="admin--form-group">
- <label class="admin--form-label">파일첨부</label>
- <div v-if="attachedFiles.length > 0" class="admin--file-list">
- <div v-for="(file, index) in attachedFiles" :key="'new-' + index" class="admin--file-item">
- <span class="admin--file-name">{{ file.name }}</span>
- <span class="admin--file-size">({{ formatFileSize(file.size) }})</span>
- <button type="button" class="admin--btn-remove-file" @click="removeFile(index)">
- 삭제
- </button>
- </div>
- </div>
- <input
- ref="fileInput"
- type="file"
- multiple
- class="admin--form-file-hidden"
- @change="handleFileAdd"
- >
- <button type="button" class="admin--btn admin--btn-secondary" @click="triggerFileInput">
- 파일 추가
- </button>
- </div>
- <!-- 버튼 영역 -->
- <div class="admin--form-actions">
- <button
- type="submit"
- class="admin--btn admin--btn-primary"
- :disabled="isSaving"
- >
- {{ isSaving ? '저장 중...' : '확인' }}
- </button>
- <button
- type="button"
- class="admin--btn admin--btn-secondary"
- @click="goToList"
- >
- 목록
- </button>
- </div>
- <!-- 성공/에러 메시지 -->
- <div v-if="successMessage" class="admin--alert admin--alert-success">
- {{ successMessage }}
- </div>
- <div v-if="errorMessage" class="admin--alert admin--alert-error">
- {{ errorMessage }}
- </div>
- </form>
- </div>
- </template>
- <script setup>
- import { ref, onMounted } from 'vue'
- import { useRoute, useRouter } from 'vue-router'
- import SunEditor from '~/components/admin/SunEditor.vue'
- import DatePicker from '~/components/admin/DatePicker.vue'
- definePageMeta({
- layout: 'admin',
- middleware: ['auth']
- })
- const route = useRoute()
- const router = useRouter()
- const { get, put, upload } = useApi()
- const isLoading = ref(true)
- const isSaving = ref(false)
- const successMessage = ref('')
- const errorMessage = ref('')
- const attachedFiles = ref([])
- const existingFiles = ref([])
- const fileInput = ref(null)
- const formData = ref({
- category: '',
- allow_comment: false,
- is_notice: false,
- name: '',
- email: '',
- start_date: '',
- end_date: '',
- title: '',
- content: '',
- file_urls: []
- })
- const loadEvent = async () => {
- isLoading.value = true
- const id = route.params.id
- const { data, error } = await get(`/board/event/${id}`)
- console.log('[EventEdit] 데이터 로드:', { data, error })
- if (data?.success && data?.data) {
- const event = data.data
- formData.value = {
- category: event.category || '',
- allow_comment: Boolean(event.allow_comment),
- is_notice: Boolean(event.is_notice),
- name: event.name || '',
- email: event.email || '',
- start_date: event.start_date || '',
- end_date: event.end_date || '',
- title: event.title || '',
- content: event.content || '',
- file_urls: event.file_urls || []
- }
- existingFiles.value = event.file_urls || []
- console.log('[EventEdit] 로드 성공')
- }
- isLoading.value = false
- }
- const triggerFileInput = () => {
- fileInput.value?.click()
- }
- const handleFileAdd = (event) => {
- const files = Array.from(event.target.files)
- attachedFiles.value.push(...files)
- event.target.value = ''
- }
- const removeFile = (index) => {
- attachedFiles.value.splice(index, 1)
- }
- const removeExistingFile = (index) => {
- existingFiles.value.splice(index, 1)
- }
- const formatFileSize = (bytes) => {
- if (bytes === 0) return '0 Bytes'
- const k = 1024
- const sizes = ['Bytes', 'KB', 'MB', 'GB']
- const i = Math.floor(Math.log(bytes) / Math.log(k))
- return Math.round(bytes / Math.pow(k, i) * 100) / 100 + ' ' + sizes[i]
- }
- const handleSubmit = async () => {
- successMessage.value = ''
- errorMessage.value = ''
- if (!formData.value.category) {
- errorMessage.value = '카테고리를 선택하세요.'
- return
- }
- if (!formData.value.title) {
- errorMessage.value = '제목을 입력하세요.'
- return
- }
- if (!formData.value.content) {
- errorMessage.value = '내용을 입력하세요.'
- return
- }
- if (!formData.value.start_date || !formData.value.end_date) {
- errorMessage.value = '기간을 입력하세요.'
- return
- }
- isSaving.value = true
- try {
- let fileUrls = [...existingFiles.value]
- // 새 파일 업로드
- if (attachedFiles.value.length > 0) {
- for (const file of attachedFiles.value) {
- const formDataFile = new FormData()
- formDataFile.append('file', file)
- const { data: uploadData, error: uploadError } = await upload('/upload/event-file', formDataFile)
- if (uploadError) {
- errorMessage.value = `파일 업로드에 실패했습니다: ${file.name}`
- isSaving.value = false
- return
- }
- if (!uploadData?.success || !uploadData?.data?.url) {
- errorMessage.value = '파일 업로드 응답이 올바르지 않습니다.'
- isSaving.value = false
- return
- }
- fileUrls.push({
- name: file.name,
- url: uploadData.data.url,
- size: file.size
- })
- }
- }
- // content에서 도메인 제거
- let contentToSave = formData.value.content
- if (contentToSave) {
- // http://도메인 또는 https://도메인 제거
- contentToSave = contentToSave.replace(/https?:\/\/[^\/]+/g, '')
- }
- const submitData = {
- ...formData.value,
- content: contentToSave,
- file_urls: fileUrls
- }
- const id = route.params.id
- const { data, error } = await put(`/board/event/${id}`, submitData)
- if (error) {
- errorMessage.value = error.message || '수정에 실패했습니다.'
- } else {
- successMessage.value = '이벤트가 수정되었습니다.'
- setTimeout(() => {
- router.push('/admin/board/event')
- }, 1000)
- }
- } catch (error) {
- errorMessage.value = '서버 오류가 발생했습니다.'
- console.error('Save error:', error)
- } finally {
- isSaving.value = false
- }
- }
- const goToList = () => {
- router.push('/admin/board/event')
- }
- onMounted(() => {
- loadEvent()
- })
- </script>
|